package com.chuangke.admin.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.dao.SysDictDaoService;
import com.chuangke.admin.data.builder.DeptDataBuilder;
import com.chuangke.admin.data.builder.UserDataBuilder;
import com.chuangke.admin.entity.SysDict;
import com.chuangke.admin.entity.SysDictType;
import com.chuangke.admin.service.SysDictService;
import com.chuangke.admin.service.SysDictTypeService;
import com.chuangke.common.constant.CacheKeyConstants;
import com.chuangke.common.constant.ColumnConstants;
import com.chuangke.common.data.builder.DataBuildConfig;
import com.chuangke.common.data.builder.DataBuilder;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.db.page.PageUtil;
import com.chuangke.framework.model.Record;
import com.chuangke.framework.util.TreeUtils;
import com.chuangke.metadata.entity.SysGridColumn;
import com.chuangke.metadata.entity.SysGridModel;
import com.chuangke.metadata.service.SysGridModelService;

import cn.hutool.core.bean.BeanDesc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.util.StrUtil;

@Service
public class SysDictServiceImpl implements SysDictService {

	@Autowired
	private SysDictTypeService sysDictTypeService;

	@Autowired
	private SysDictDaoService sysDictDaoService;

	@Autowired
	private SysGridModelService sysGridModelService;

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void autoSave(SysDict record) {
		Map<String, SysDict> dictMap = findMap(record.getType());
		if (dictMap.get(record.getCode()) == null) {
			save(record);
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void save(SysDict record) {
		((SysDictService) AopContext.currentProxy()).doSave(record);
		level(record.getType());
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void doSave(SysDict record) {
		sysDictDaoService.saveOrUpdate(record);
	}

	/**
	 * 对数据进行分级，更新数据的level,is_last_level,data_domain
	 */
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void level(String type) {
		List<SysDict> list = findTree(type);
		List<SysDict> newList = new ArrayList<>();
		TreeUtils.level("", 0, list, newList);
		doUpdateBatch(newList);
	}

	@Override
	public List<SysDict> findListWithChildren(String type) {
		List<SysDict> list = findTree(type);
		return TreeUtils.treeToList(list);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void doUpdateBatch(List<SysDict> list) {
		sysDictDaoService.updateBatchById(list);
	}

	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	@Override
	public void delete(SysDict record) {
		sysDictDaoService.removeById(record);
	}

	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	@Override
	public void delete(List<SysDict> records) {
		sysDictDaoService.removeBatchByIds(records);
	}

	@Cacheable(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	@Override
	public Map<String, List<SysDict>> findAll() {
		List<SysDict> allList = sysDictDaoService.list();
		return allList.stream().collect(Collectors.groupingBy(SysDict::getType));
	}

	@Override
	public Map<String, SysDict> findMap(String type) {
		Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();
		map = map == null ? new HashMap<>() : map;
		List<SysDict> list = map.get(type);

		DataBuilder.of(list).appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
				new DataBuildConfig("updatedBy", "updatedByName")).build();

		return list == null ? new HashMap<>() : list.stream().collect(Collectors.toMap(SysDict::getCode, a -> a));
	}

	@Override
	public Map<String, SysDict> findMap(String type, String deptId) {
		Map<String, SysDict> map = ((SysDictService) AopContext.currentProxy()).findMap(type);
		return map.values().stream().filter(dict -> deptId.equals(dict.getDeptId()))
				.collect(Collectors.toMap(SysDict::getCode, a -> a));
	}

	@Override
	public IPage<SysDict> findPage(CkPageQuery page) {
		Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();

		String label = (String) page.getOrDefault("label", "");
		String type = (String) page.getOrDefault("type", "");
		String deptId = (String) page.getOrDefault("deptId", "");
		List<SysDict> result = new ArrayList<>();

		if (StrUtil.isEmpty(type)) {
			for (List<SysDict> list : map.values()) {
				result.addAll(list);
			}
		} else {
			result = map.get(type);
		}

		result = result == null ? new ArrayList<>() : result;

		List<SysDict> list = result.stream().filter(dict -> ((StrUtil.isEmpty(label)
				|| (StrUtil.isNotEmpty(label) && dict.getName().matches(".*" + label + ".*"))
				|| (StrUtil.isNotEmpty(label) && dict.getCode().matches(".*" + label + ".*")))
				&& (StrUtil.isEmpty(deptId) || (StrUtil.isNotEmpty(deptId)
						&& StrUtil.isNotEmpty(dict.getDeptId()) && dict.getDeptId().matches(".*" + deptId + ".*")))))
				.collect(Collectors.toList());

		DataBuilder.of(list)
				.appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
						new DataBuildConfig("updatedBy", "updatedByName"))
				.appendBuilder(new DeptDataBuilder(), new DataBuildConfig("deptId", "deptName")).build();
		return PageUtil.findPage(page, list);
	}

    @Override
    public IPage<SysDict> findPageByColumns(CkPageQuery page) {
        Set<String> keys = page.keySet() ;
        BeanDesc beanDesc = BeanUtil.getBeanDesc(SysDict.class); // 获取 Bean 描述信息
        Map<String, PropDesc> propMap = beanDesc.getPropMap(false) ;
        Set<String> dictColumns = propMap.keySet() ;

        Set<String> intersectionSet = new HashSet<>(keys);
        intersectionSet.retainAll(dictColumns);

        Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();
        List<SysDict> result = new ArrayList<>();
        for (List<SysDict> list : map.values()) {
            result.addAll(list);
        }

		if(CollectionUtils.isEmpty(intersectionSet)){
			return PageUtil.findPage(page, result) ;
		}

		List<SysDict> list = result.stream().filter(dict -> {
			for(String column : intersectionSet){
				Object dictValue = BeanUtil.getFieldValue(dict, column) ;
				Object queryValue = page.get(column) ;
				if(!dictValue.equals(queryValue)){
					return false ;
				}
			}
			return true ;
			}).collect(Collectors.toList());

		DataBuilder.of(list)
				.appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
						new DataBuildConfig("updatedBy", "updatedByName"))
				.appendBuilder(new DeptDataBuilder(), new DataBuildConfig("deptId", "deptName")).build();
		return PageUtil.findPage(page, list);
    }

	@Override
	public List<SysDict> findByName(String lable) {
		Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();

		List<SysDict> result = new ArrayList<>();
		map.values().forEach(dictList -> result
				.addAll(dictList.stream().filter(dict -> dict.getName().contains(lable)).collect(Collectors.toList())));

		return result;
	}
	
	@Override
	public List<SysDict> findByName(String type,String lable) {
		if(StrUtil.isBlank(lable)) {
			return new ArrayList<>() ;
		}
		Map<String, SysDict> map = ((SysDictService) AopContext.currentProxy()).findMap(type) ;
		return map.values().stream().filter(dict -> dict.getName().equals(lable)).collect(Collectors.toList());
	}

	@Override
	public List<SysDict> findKinshipTree(String type, Collection<String> ids) {
		Map<String, SysDict> noChildMap = ((SysDictService) AopContext.currentProxy()).findMap(type);
		noChildMap = noChildMap.values().stream().collect(Collectors.toMap(SysDict::getId, a -> a));
		List<SysDict> withChildrenList = findListWithChildren(type);
		return TreeUtils.findKinshipTree(noChildMap, withChildrenList, ids);
	}

	@Override
	public List<SysDict> findTree(String type) {
		Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();
		List<SysDict> dicts = map.get(type);
		dicts = dicts == null ? new ArrayList<>() : dicts;

		DataBuilder.of(dicts).appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
				new DataBuildConfig("updatedBy", "updatedByName")).build();

		return TreeUtils.buildTree(dicts);
	}

	@Override
	public List<SysDict> findLastLevel(String type) {
		List<SysDict> treeList = findTree(type);

		List<SysDict> lastLevelDeptList = new ArrayList<>();
		TreeUtils.findLastLevel(treeList, lastLevelDeptList);

		return lastLevelDeptList;
	}

	@Override
	public List<SysDict> findByLevel(String type, int level) {
		Map<String, List<SysDict>> map = ((SysDictService) AopContext.currentProxy()).findAll();
		List<SysDict> dicts = map.get(type);

		return TreeUtils.findByLevel(dicts, level);
	}

	@Override
	public List<SysDict> findTreeByType(String type, String label, String deptId) {
		List<SysDict> list = findListWithChildren(type);
		list = list.stream().filter(item -> (StrUtil.isEmpty(label)
				|| (StrUtil.isNotEmpty(label) && item.getName().matches(".*" + label + ".*")))
				&& (StrUtil.isEmpty(deptId) || (StrUtil.isNotEmpty(deptId) && item.getDeptId().equals(deptId))))
				.collect(Collectors.toList());

		DataBuilder.of(list)
				.appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
						new DataBuildConfig("updatedBy", "updatedByName"))
				.appendBuilder(new DeptDataBuilder(), new DataBuildConfig("deptId", "deptName")).build();

		return list;
	}

	@Override
	public Map<String, Record> getStandardData(String type) {
		Map<String, SysDict> map = ((SysDictService) AopContext.currentProxy()).findMap(type);
		List<Record> list = new ArrayList<>(map.size());
		for (SysDict sd : map.values()) {
			Record record = new Record();
			record.put(ColumnConstants.COL_NAME_CODE, sd.getId());
			record.put(ColumnConstants.COL_NAME_NAME, sd.getName());
			record.put(ColumnConstants.COL_NAME_LEVEL, sd.getLevel());
			record.put(ColumnConstants.COL_NAME_IS_LAST_LEVEL, sd.getIsLastLevel());
			record.put(ColumnConstants.COL_NAME_DATA_DOMAIN, sd.getDataDomain());
			record.put(ColumnConstants.COL_NAME_ID, sd.getId());
			record.put(ColumnConstants.COL_NAME_PARENT_ID, sd.getParentId());
			list.add(record);
		}
		return list.stream().collect(Collectors.toMap(record -> record.getStr(ColumnConstants.COL_NAME_CODE), a -> a));
	}

	@Override
	public Map<String, Object> findTreeWithView(String type, String deptId, String name) {
		List<SysDict> datas = null;
		
		Map<String,SysDict> dataMap = ((SysDictService) AopContext.currentProxy()).findMap(type) ;
		
		datas = dataMap.values().stream().filter(d -> {
			if(!StrUtil.isBlank(name) && !d.getCode().contains(name) && !d.getName().contains(name)) {
				return false ;
			}
            return StrUtil.isBlank(deptId) || deptId.equals(d.getDeptId());
        }).collect(Collectors.toList()) ;
		
		List<String> ids = datas.stream().map(SysDict::getId).collect(Collectors.toList());
		datas = findKinshipTree(type, ids);
		
		List<SysGridColumn> columns = getColumnsByModel(type);

		Map<String, Object> result = new HashMap<>();
		result.put("datas", datas);
		result.put("columns", columns);

		return result;
	}

	private List<SysGridColumn> getColumnsByModel(String type) {
		SysDictType dictType = sysDictTypeService.findByType(type);
		List<SysGridColumn> columns = null;
		if (StrUtil.isBlank(dictType.getModelId())) {
			columns = getDefaultColumns();
		} else {
			SysGridModel model = sysGridModelService.load(dictType.getModelId());
			if (model != null) {
				columns = model.getColumns();
			} else {
				columns = getDefaultColumns();
			}
		}
		return columns;
	}

	private List<SysGridColumn> getDefaultColumns() {
		List<SysGridColumn> columns = new ArrayList<>();

		columns.add(new SysGridColumn("code", "编号", "left", "250", "0"));
		columns.add(new SysGridColumn("name", "名称", "left", "300", "0"));
		columns.add(new SysGridColumn("description", "描述", "left", "120", "0"));
		columns.add(new SysGridColumn("createdByName",	 "创建人",  "left",   "100", "0"));
		columns.add(new SysGridColumn("createdAt", 		 "创建时间","left",    "140", "0"));
		columns.add(new SysGridColumn("updatedByName", "修改人", "left", "100", "0"));
		columns.add(new SysGridColumn("updatedAt", "修改时间", "left", "140", "0"));

		return columns;
	}

	@Override
	public int getMaxIntegerCode(String type) {
		Map<String, SysDict> dictMap = findMap(type);

		if (MapUtils.isEmpty(dictMap)) {
			return 0;
		}

		List<Integer> codeList = dictMap.values().stream().map(d -> {
			try {
				return Integer.parseInt(d.getCode());
			} catch (Exception e) {
			}
			return 0;
		}).collect(Collectors.toList());

		return codeList.stream().reduce(codeList.get(0), Integer::max);
	}

	@Override
	@CacheEvict(cacheNames = CacheKeyConstants.SYS_DICT, key = "'" + CacheKeyConstants.SYS_DICT + "'")
	public void cacheEvict() {
		
	}

}
